package com.eureka.gateway.filter.gateway;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eureka.bclass.enums.SysKeyEnum;
import com.eureka.bclass.enums.SysStateEnum;
import com.eureka.bclass.util.LoggerUtil;
import com.eureka.gateway.enums.GatewayFilterOrderEnum;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Description: TODO 入参全局过滤器，不需要在配置文件中配置，作用于所有的请求
 * @Author: lixing
 * @Date: 2021/6/24 10:25
 */
public class MyRequestParamsGatewayFilter implements GatewayFilter, Ordered {
    /** 通过构造函数初始化参数配置类 */
    private MyRequestParamsGatewayFilterFactoryConfig myRequestParamsGatewayFilterFactoryConfig;
    public MyRequestParamsGatewayFilter(MyRequestParamsGatewayFilterFactoryConfig myRequestParamsGatewayFilterFactoryConfig) {
        this.myRequestParamsGatewayFilterFactoryConfig = myRequestParamsGatewayFilterFactoryConfig;
    }

    /**
     * 过滤器执行的顺序，值越小，执行顺序越靠前
     */
    @Override
    public int getOrder() {
        return GatewayFilterOrderEnum.MY_REQUEST_PARAMS.getValue();
    }

    /**
     * TODO 网关“前置”过滤器逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //
        String schema = request.getURI().getScheme();
        if ((!"http".equalsIgnoreCase(schema) && !"https".equalsIgnoreCase(schema))) {
            return chain.filter(exchange);
        }
        // 请求地址
        String reqPath = request.getPath().toString();
        // 请求方式：POST
        String method = request.getMethodValue();
        // 媒体类型：浏览器以JSON形式对资源进行解析
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        // 请求中的参数是否需要解密
        exchange.getAttributes().put("isDecrypt", myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt());
        if (SysKeyEnum.GET.getKey().equalsIgnoreCase(method)) {
            /*
             * GET 请求
             */
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                LoggerUtil.info("解密成功");
            }
            return chain.filter(exchange)
                // TODO 网关“后置”过滤器逻辑
                .then(Mono.fromRunnable(() -> {
                    if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsPring())) {
                        LoggerUtil.info("");
                        LoggerUtil.info(MessageFormat.format("入参全局过滤器 ---- start [GET]：{0}", reqPath));
                        if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                            LoggerUtil.info(MessageFormat.format("入参解密前: {0}", JSONObject.toJSONString(queryParams)));
                            LoggerUtil.info(MessageFormat.format("入参解密后: {0}", JSONObject.toJSONString(queryParams)));
                        } else {
                            LoggerUtil.info(MessageFormat.format("入参: {0}", JSONObject.toJSONString(queryParams)));
                        }
                        LoggerUtil.info("入参全局过滤器 ---- end");
                    }
            }));
        } else if (SysKeyEnum.POST.getKey().equalsIgnoreCase(method) && MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
            ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
            // ServerRequest serverRequest = new DefaultServerRequest(exchange);
            // 读取 body 中的内容并修改
            AtomicReference<JSONObject> bodyJsonObj = new AtomicReference<>(new JSONObject());
            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
                bodyJsonObj.set(JSON.parseObject(body));
                if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                    LoggerUtil.info("解密成功");
                }
                return Mono.just(JSONObject.toJSONString(bodyJsonObj));
            });
            /*
             * TODO 下面的将请求体再次封装写回到request里，传到下一级，否则，由于请求体已被消费，后续的服务将取不到值
             * CacheBodyGlobalFilter这个全局过滤器的目的就是把原有的request请求中的body内容读出来，并且使用ServerHttpRequestDecorator这个请求装饰器对request进行包装，重写getBody方法，并把包装后的请求放到过滤器链中传递下去。
             * 这样后面的过滤器中再使用exchange.getRequest().getBody()来获取body时，实际上就是调用的重载后的getBody方法，获取的最先已经缓存了的body数据
             */
            BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            // the new content type will be computed by bodyInserter and then set in the request decorator
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            return bodyInserter.insert(outputMessage,  new BodyInserterContext()).then(Mono.defer(() -> {
                ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                    @Override
                    public HttpHeaders getHeaders() {
                        long contentLength = headers.getContentLength();
                        HttpHeaders httpHeaders = new HttpHeaders();
                        httpHeaders.putAll(super.getHeaders());
                        if (contentLength > 0) {
                            httpHeaders.setContentLength(contentLength);
                        } else {
                            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                        }
                        return httpHeaders;
                    }
                    @Override
                    public Flux<DataBuffer> getBody() {
                        Flux<DataBuffer> dataBufferFlux = outputMessage.getBody();
                        return dataBufferFlux;
                    }
                };
                // 从原始交换对象获得一个新的 ServerWebExchange 实例
                ServerWebExchange serverWebExchangeMutable = exchange.mutate().request(serverHttpRequestDecorator).build();
                return chain.filter(serverWebExchangeMutable)
                    // TODO 网关“后置”过滤器逻辑
                    .then(Mono.fromRunnable(() -> {
                        if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsPring())) {
                            LoggerUtil.info("");
                            LoggerUtil.info(MessageFormat.format("MyRequestParamsGatewayFilter ---- start [POST]：{0}", reqPath));
                            if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                                LoggerUtil.info(MessageFormat.format("请求入参解密前: {0}", bodyJsonObj.get().toJSONString()));
                                LoggerUtil.info(MessageFormat.format("请求入参解密后: {0}", bodyJsonObj.get().toJSONString()));
                            } else {
                                LoggerUtil.info(MessageFormat.format("请求入参: {0}", bodyJsonObj.get().toJSONString()));
                            }
                            LoggerUtil.info("MyRequestParamsGatewayFilter ---- end");
                            LoggerUtil.info("");
                        }
                }));
            }));
        } else {
            return chain.filter(exchange);
        }
    }

}
